/*******************************************************************************
* Copyright 2007-2013 See AUTHORS file.
 * 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* 
*   http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package xworker.html;

import freemarker.template.TemplateException;
import org.apache.commons.io.FileUtils;
import org.xmeta.ActionContext;
import org.xmeta.Thing;
import org.xmeta.World;
import org.xmeta.util.UtilString;
import xworker.html.module.ModuleRequire;
import xworker.lang.util.UtilTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HtmlActions {
	@SuppressWarnings("unchecked")
	public static void addHead(Thing self, String key , String code, ActionContext actionContext) throws IOException, TemplateException{
		boolean exists = false;
        List<Map<String, String>> heads = (List<Map<String, String>>) actionContext.get("heads");
        
        if(heads == null || key == null || "".equals(key) || code == null || "".equals(code)){
        	return;
        }
        
        for(Map<String, String> head : heads){
            if(key.equals(head.get("name"))){
                exists = true;
                break;
            }                    
        }
        
        if(!exists){
	        Map<String, String> head = new HashMap<>();
	        head.put("name",  key);
	        head.put("value", UtilTemplate.processString(actionContext, code));
	        
	        if(self.getBoolean("first")){
	        	heads.add(0, head);
	        }else{
	        	heads.add(head);
	        }
        }
	}
	
	public static void requireModule(String module, String version, ActionContext actionContext) {
		if(module == null) {
			return;
		}
		
		List<ModuleRequire> requiredModules = actionContext.getObject("requiredModules");
		if(requiredModules != null) {
			boolean have = false;
			for(ModuleRequire mr : requiredModules) {
				have = mr.equals(module, version);
				if(have) {
					break;
				}
			}
			
			if(!have) {
				ModuleRequire mr = new ModuleRequire(module, version);
				requiredModules.add(mr);
			}
		}
	}
	
	//requireModule的动作实现
	public static void requireModule(ActionContext actionContext) {
		Thing self = actionContext.getObject("self");
		String module = self.doAction("getModule", actionContext);
		String version = self.doAction("getVersion", actionContext);
		
		requireModule(module, version, actionContext);
	}
	
	public static void addHead(ActionContext actionContext) throws IOException, TemplateException{
		Thing self = (Thing) actionContext.get("self");
		String key = self.getString("key");
		String code = self.getString("code");
		
		addHead(self, key, code, actionContext);
	}
	
	@SuppressWarnings("unchecked")
	public static void addBottom(String key , String  code, ActionContext actionContext) throws IOException, TemplateException{
		boolean exists = false;
        List<Map<String, String>> bottons = (List<Map<String, String>>) actionContext.get("bottoms");
        
        if(bottons == null || key == null || "".equals(key) || code == null || "".equals(code)){
        	return;
        }
        
        for(Map<String, String> botton : bottons){
            if(key.equals(botton.get("name"))){
                exists = true;
                break;
            }                    
        }
        
        if(!exists){
	        Map<String, String> botton = new HashMap<>();
	        botton.put("name",  key);
	        botton.put("value", UtilTemplate.processString(actionContext, code));
	        bottons.add(botton);
        }
	}
	
	public static void addBottom(ActionContext actionContext) throws IOException, TemplateException{
		Thing self = (Thing) actionContext.get("self");
		String key = self.getString("key");
		String code = self.getString("code");
		
		addBottom(key, code, actionContext);
	}
	
	public static void addScriptThing(String key, ActionContext actionContext){		
		Thing self = actionContext.getObject("self");		
		Thing script = (Thing) self.doAction("getScriptThing", actionContext);
		boolean first = self.getBoolean("first");
		addScriptThing(key, script, first, actionContext);		
	}
	
	public static void addScriptThing(String key, Thing script, boolean first, ActionContext actionContext) {
		Thing scripts = actionContext.getObject(key);
		if(scripts == null){
			return;
		}
		
		if(script != null){
			if(first){
				scripts.addChild(script, 0, false);
			}else{
				scripts.addChild(script, false);
			}
		}			
	}
	
	public static void addHeadScriptThing(ActionContext actionContext){
		addScriptThing(HtmlConstants.HTML_HEADS_JAVASCRIPT_THING, actionContext);
	}
	
	public static void addBottonScriptThing(ActionContext actionContext){
		addScriptThing(HtmlConstants.HTML_BOTTOM_JAVASCRIPT_THING, actionContext);
	}
	
	@SuppressWarnings("unchecked")
	public static void addHtmlAttributes(ActionContext actionContext){
		Thing self = actionContext.getObject("self");
		
		Map<String, String> headAttributes = actionContext.getObject(HtmlConstants.HTML_ATTRIBUTES);
		String key = self.getString("key");
		String value = self.getString("value");
		headAttributes.put(key, value);
	}
	
	public static void addBodyAttributes(ActionContext actionContext){
		Thing self = actionContext.getObject("self");
		
		Map<String, String> headAttributes = actionContext.getObject(HtmlConstants.BODY_ATTRIBUTES);
		String key = self.getString("key");
		String value = self.getString("value");
		headAttributes.put(key, value);
	}
	
	public static String generateHtml(ActionContext actionContext) throws Throwable {
		Thing self = actionContext.getObject("self");
		
		Thing htmlThing = self.doAction("getHtmlThing", actionContext);
		return processViewThingToString(htmlThing, actionContext);
	}

	public static String processViewThingToString(Thing formObject, ActionContext actionContext) throws Throwable{
		String templateName = processViewThingTemplate(formObject, actionContext);

		try{
			return UtilTemplate.process(actionContext, templateName, "freemarker","UTF-8");
		}catch(Throwable t){
			formObject.setData("_form_LastModified_", null);
			formObject.setData("_form_TemplateName_", null);

			throw t;
		}
	}

	/**
	 * 处理表单事物生成Freemaker模板，并返回模板的路径。
	 */
	public static String processViewThingTemplate(Thing formObject, ActionContext actionContext) throws Throwable{
		Long formLastModified = formObject.getData("_form_LastModified_");
		String templateName = formObject.getData("_form_TemplateName_");
		//File f = (File) formObject.getData("_form_file");
		if(formLastModified == null || templateName == null || formLastModified < formObject.getMetadata().getLastModified()){
			//取表单的临时文件名
			StringBuilder fileNameBuff = new StringBuilder(World.getInstance().getPath());
			StringBuilder tName = new StringBuilder();
			tName.append("/work/forms");
			tName.append("/").append(UtilString.getThingManagerFileName(formObject));
			tName.append("/").append(formObject.getRoot().getMetadata().getPath().replace('.', '/'));
			tName.append("/").append(formObject.getMetadata().getPath().hashCode());
			tName.append("/").append(formObject.getMetadata().getName());
			tName.append(".ftl");
			String fileName = fileNameBuff.append(tName).toString();

			File file = new File(fileName);
			if(!file.exists() || file.lastModified() < formObject.getMetadata().getLastModified()){
				FileUtils.forceMkdirParent(file);
				try{
					//List formElements = UtilView.
					//System.out.println("org.xmeta.util.UtilForm:114 re formed");
					String ftl = formObject.doAction("toHtml", actionContext);
					OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
					w.write(ftl);
					w.flush();
					w.close();
				}catch(Exception e){
					e.printStackTrace();
				}
			}

			templateName = tName.toString();
			//formObject.setData("_form_file", file);
			formObject.setData("_form_LastModified_", formObject.getMetadata().getLastModified());
			formObject.setData("_form_TemplateName_", templateName);

			UtilTemplate.removeTempalteCache(templateName);
		}

		return templateName;
	}
}